﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Timtopia.DGMLPosterView;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.VisualStudio.GraphModel;
using System.Diagnostics;

namespace UnitTests
{
    [TestClass]
    public class PowerGraphHeuristicVersusOptimalTests
    {
        [TestMethod]
        public void Study2Graphs()
        {
            string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
            var directory = System.IO.Path.GetDirectoryName(path) + @"\..\..\ReferenceGraphs\study2-graphs";
            directory = System.IO.Path.GetFullPath(directory);
            var glob = directory + @"\*edgeweight*crossingweight*";
            var files = Glob(glob);
            Assert.AreEqual(24, files.Count());

            Console.WriteLine("graphNumber, edgeWeight, crossingWeight, originalEdgeCount, optimalModuleCount, optimalPowerEdgeCount, optimalCrossingCount, heuristicModuleCount, heuristicPowerEdgeCount, heuristicCrossingCount, moduleLimit");
            foreach (var f in files)
            {
                var fname = Path.GetFileName(f);
                var fileNameExpression = new Regex(@"([^_]+)_edgeweight(\d+)_crossingweight(\d+)");
                Assert.IsTrue(fileNameExpression.IsMatch(fname));
                var m = fileNameExpression.Match(fname);
                var graphNumber = m.Groups[1].ToString();
                var edgeWeight = int.Parse(m.Groups[2].ToString());
                var crossingWeight = int.Parse(m.Groups[3].ToString());

                GetDecompositionStats(f, graphNumber, edgeWeight, crossingWeight, 0);
            }
        }
        public static void GetDecompositionStats(string f, string graphNumber, int edgeWeight, int crossingWeight, int moduleLimit)
        {
            var g = PowerGraphDecomposition.LoadSimpleDescription(f, originalEdges: true);
            var originalEdgeCount = g.Links.Count;
            double cw = double.Parse(crossingWeight.ToString()) / double.Parse(edgeWeight.ToString());
            PowerGraphDecomposition.Apply(g, cw);

            var heuristicModuleCount = g.AllGroups.Count();


            var heuristicPowerEdges = from l in g.Links
                                      where l.HasCategory(PowerGraphDecomposition.PowerEdgeCategory)
                                      select l;

            var heuristicPowerEdgeCount = heuristicPowerEdges.Count();
            var heuristicCrossingCount = heuristicPowerEdges.Sum(l => ModuleCrossingCount(l));

            g = PowerGraphDecomposition.LoadSimpleDescription(f, originalEdges: false);

            var optimalPowerEdges = from l in g.Links
                                    where l.HasCategory(PowerGraphDecomposition.PowerEdgeCategory)
                                    select l;
            var optimalPowerEdgeCount = optimalPowerEdges.Count();
            var optimalCrossingCount = optimalPowerEdges.Sum(l => ModuleCrossingCount(l));

            var optimalModuleCount = g.AllGroups.Count();

            Console.WriteLine("Graph{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}", graphNumber, edgeWeight, crossingWeight, originalEdgeCount, optimalModuleCount, optimalPowerEdgeCount, optimalCrossingCount, heuristicModuleCount, heuristicPowerEdgeCount, heuristicCrossingCount, moduleLimit);
        }

        public static void GetDecompositionStats(Graph g, string graphNumber, int edgeWeight, int crossingWeight, int moduleLimit)
        {
            var originalEdgeCount = g.Links.Count;
            double cw = double.Parse(crossingWeight.ToString()) / double.Parse(edgeWeight.ToString());
            var sw = new Stopwatch();
            sw.Start();
            PowerGraphDecomposition.Apply(g, cw);
            var ellapsedTime = sw.ElapsedMilliseconds;

            var heuristicModuleCount = g.AllGroups.Count();


            var heuristicPowerEdges = from l in g.Links
                                      where l.HasCategory(PowerGraphDecomposition.PowerEdgeCategory)
                                      select l;

            var heuristicPowerEdgeCount = heuristicPowerEdges.Count();
            var heuristicCrossingCount = heuristicPowerEdges.Sum(l => ModuleCrossingCount(l));
            Console.WriteLine("Graph{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}", graphNumber, edgeWeight, crossingWeight, originalEdgeCount, heuristicModuleCount, heuristicPowerEdgeCount, heuristicCrossingCount, moduleLimit, ellapsedTime);
        }

        private static int ModuleCrossingCount(GraphLink l)
        {
            var ancestor = Graph.FindCommonAncestor(new[] { l.Source, l.Target });
            var sourceAncestors = l.Source.FindAncestorGroups();
            int crossingCount = 0;
            foreach (var a in sourceAncestors)
            {
                if (a == ancestor) break;
                crossingCount++;
            }
            var targetAncestors = l.Target.FindAncestorGroups();
            foreach (var a in targetAncestors)
            {
                if (a == ancestor) break;
                crossingCount++;
            }
            return crossingCount;
        }

        [TestMethod]
        public void PowerLawGraphs()
        {
            string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
            var directory = System.IO.Path.GetDirectoryName(path) + @"\..\..\ReferenceGraphs\july-results";
            directory = System.IO.Path.GetFullPath(directory);
            var glob = directory + @"\*out*";
            var files = Glob(glob);
            Assert.AreEqual(540, files.Count());

            Console.WriteLine("graphNumber, edgeWeight, crossingWeight, originalEdgeCount, optimalModuleCount, optimalPowerEdgeCount, optimalCrossingCount, heuristicModuleCount, heuristicPowerEdgeCount, heuristicCrossingCount");
            foreach (var f in files)
            {
                var fileNameExpression = new Regex(@"graph-(\d\d).out-(\d+)-(\d+)-(\d+)-99999");
                Assert.IsTrue(fileNameExpression.IsMatch(f));
                var m = fileNameExpression.Match(f);
                var graphNumber = m.Groups[1].ToString();
                var edgeWeight = int.Parse(m.Groups[2].ToString());
                var crossingWeight = int.Parse(m.Groups[3].ToString());
                var moduleLimit = int.Parse(m.Groups[4].ToString());

                GetDecompositionStats(f, graphNumber, edgeWeight, crossingWeight, moduleLimit);
            }
        }
        /// <summary>
        /// return a list of files that matches some wildcard pattern, e.g. 
        /// C:\p4\software\dotnet\tools\*\*.sln to get all tool solution files
        /// </summary>
        /// <param name="glob">pattern to match</param>
        /// <returns>all matching paths</returns>
        public static IEnumerable<string> Glob(string glob)
        {
            foreach (string path in Glob(PathHead(glob) + DirSep, PathTail(glob)))
                yield return path;
        }

        /// <summary>
        /// uses 'head' and 'tail' -- 'head' has already been pattern-expanded
        /// and 'tail' has not.
        /// </summary>
        /// <param name="head">wildcard-expanded</param>
        /// <param name="tail">not yet wildcard-expanded</param>
        /// <returns></returns>
        public static IEnumerable<string> Glob(string head, string tail)
        {
            if (PathTail(tail) == tail)
                foreach (string path in Directory.GetFiles(head, tail).OrderBy(s => s))
                    yield return path;
            else
                foreach (string dir in Directory.GetDirectories(head, PathHead(tail)).OrderBy(s => s))
                    foreach (string path in Glob(Path.Combine(head, dir), PathTail(tail)))
                        yield return path;
        }

        /// <summary>
        /// shortcut
        /// </summary>
        static char DirSep = Path.DirectorySeparatorChar;

        /// <summary>
        /// return the first element of a file path
        /// </summary>
        /// <param name="path">file path</param>
        /// <returns>first logical unit</returns>
        static string PathHead(string path)
        {
            // handle case of \\share\vol\foo\bar -- return \\share\vol as 'head'
            // because the dir stuff won't let you interrogate a server for its share list
            // FIXME check behavior on Linux to see if this blows up -- I don't think so
            if (path.StartsWith("" + DirSep + DirSep))
                return path.Substring(0, 2) + path.Substring(2).Split(DirSep)[0] + DirSep + path.Substring(2).Split(DirSep)[1];

            return path.Split(DirSep)[0];
        }

        /// <summary>
        /// return everything but the first element of a file path
        /// e.g. PathTail("C:\TEMP\foo.txt") = "TEMP\foo.txt"
        /// </summary>
        /// <param name="path">file path</param>
        /// <returns>all but the first logical unit</returns>
        static string PathTail(string path)
        {
            if (!path.Contains(DirSep))
                return path;

            return path.Substring(1 + PathHead(path).Length);
        }
    }
}
